﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Asplode.GameEntity.Characters;
using BEPUphysics.Entities;
using Microsoft.Xna.Framework;
using Asplode.GameEntity;
using Asplode.InputHandler;
using Microsoft.Xna.Framework.Input;
using FlyingGamePC.GameEntity.Weapons;
using Microsoft.Xna.Framework.Graphics;
using BEPUphysics.Events;
using System.Diagnostics;

namespace Asplode.GameWorld
{
    public class GameWorldEventHandler
    {
        private static GameWorld GameWorld;
        
        public GameWorldEventHandler(GameWorld gw)
        {
            GameWorld = gw;
        }

        public void subscribe(EventDispatcher EventDispatcher)
        {
            EventDispatcher.BoxSpawn += new EventDispatcher.BoxSpawnHandler(EventDispatcher_BoxSpawn);
            EventDispatcher.CursorSpawn += new EventDispatcher.CursorSpawnHandler(EventDispatcher_CursorSpawn);
            EventDispatcher.Damage += new EventDispatcher.DamageHandler(EventDispatcher_Damage);
            EventDispatcher.HumanSpawn += new EventDispatcher.HumanSpawnHandler(EventDispatcher_HumanSpawn);
            EventDispatcher.Kill += new EventDispatcher.KillHandler(EventDispatcher_Kill);
            EventDispatcher.MortarShot += new EventDispatcher.MortarShotHandler(EventDispatcher_MortarShot);
            EventDispatcher.BoxShot += new EventDispatcher.BoxShotHandler(EventDispatcher_BoxShot);
            EventDispatcher.RemoveEntity += new EventDispatcher.RemoveEntityHandler(EventDispatcher_RemoveEntity);
            EventDispatcher.ZombieSpawn += new EventDispatcher.ZombieSpawnHandler(EventDispatcher_ZombieSpawn);
        }

        void EventDispatcher_BoxShot(Entity Box, Vector3 initialForce)
        {   
            Box.eventManager.addEventHook(new EventHandlerInitialCollisionDetected(GameWorld.boxShotHit)); 
        }

        void EventDispatcher_CursorSpawn(Entity Player, Entity Cursor)
        {
            GameWorld.EntList.Add(Cursor);
        }

        void EventDispatcher_BoxSpawn(Box box, Vector3 initialForce)
        {
            GameWorld.EntList.Add(box);
        }

        // This may be reused to implement healing actions performed on dead players.
        void EventDispatcher_Damage(Character source, Character target, float damage)
        {
            target.health -= damage;
            System.Console.WriteLine("" + source.GetHashCode() + " deals " + damage + " damage to " + target.GetHashCode() + "; " + target.health + " left. " + DateTime.Now + DateTime.Now.Millisecond);
            
            // If the target has negative health but didn't before, they were just killed.
            if (target.health <= 0 && target.health + damage > 0)
            {
                GameWorld.EventDispatcher.RaiseKill(source, target);
            }
        }

        void EventDispatcher_HumanSpawn(Entity entity)
        {
            GameWorld.EntList.Add(entity);
        }

        void EventDispatcher_Kill(Character killer, Character victim)
        {
            killer.kills++;
            victim.deaths++;

            // Teleport the target to a new location.
            Random random = new Random();
            Entity body = victim.gameObject.body;
            body.teleportTo(new Vector3(((float)random.NextDouble() - 0.5f) * 100, body.internalCenterPosition.Y, ((float)random.NextDouble() - 0.5f) * 100));
            
            victim.health = victim.startingHealth;

            System.Console.WriteLine("" + killer.GetHashCode() + " killed " + victim.GetHashCode());
        }

        void EventDispatcher_MortarShot(Entity entity, Vector3 initialForce)
        {
            GameWorld.EntList.Add(entity);
        }

        void EventDispatcher_RemoveEntity(Entity entity)
        {
            foreach (Human player in GameWorld.Humans)
            {
                if (player.gameObject.body == entity)
                {
                    GameWorld.Humans.Remove(player);
                    break;
                }
            }
            foreach (Zombie zombie in GameWorld.Zombies)
            {
                if (zombie.gameObject.body == entity)
                {
                    GameWorld.Zombies.Remove(zombie);
                    break;
                }
            }
            if (GameWorld.EntList.Contains(entity))
            {
                GameWorld.EntList.Remove(entity);
            }
        }

        void EventDispatcher_ZombieSpawn(Entity entity)
        {
            GameWorld.EntList.Add(entity);
            GameWorld.Zombies.Add(new Zombie(new GameObject(entity)));
        }

        internal static void HandleFirstPerson(InputAction action, GameTime gameTime)
        {
            GameWorld.Map.Camera.CameraMode = AsplodeCamera.Mode.FirstPerson;
        }

        internal static void HandleThirdPerson(InputAction action, GameTime gameTime)
        {
            GameWorld.Map.Camera.CameraMode = AsplodeCamera.Mode.ThirdPerson;
        }

        internal static void HandleTopDown(InputAction action, GameTime gameTime)
        {
            GameWorld.Map.Camera.CameraMode = AsplodeCamera.Mode.TopDown;
            GameWorld.Map.Camera.UpdateTopDownMode(new Vector3(0, 60, -1));
        }

        internal static void HandleGameExit(InputAction action, GameTime gameTime)
        {
            System.Environment.Exit(0);
        }

        internal static void HandleShoot(InputAction action, GameTime gameTime)
        {
            if (GameWorld.Player.weapon.CanShoot())
            {
                // For now, don't check to see if the player is targeting anyone.
                GameWorld.Player.weapon.Shoot(GameWorld.Player, null, GameWorld.GWEntityFactory);
            }
        }

        internal static void HandleWeaponSwitch(InputAction action, GameTime gameTime)
        {
            switch (action.KeyboardKey)
            {
                case Keys.D1:
                    GameWorld.Player.weapon = new BoxGun();
                    break;
                case Keys.D2:
                    GameWorld.Player.weapon = new Mortar();
                    break;
            }
        }

        internal static void HandleMoveButtonUp(InputAction action, GameTime gameTime)
        {
            Vector3 dir = Vector3.Zero;
            switch (action.KeyboardKey)
            {
                case Keys.W:
                    dir += Vector3.UnitZ;
                    break;
                case Keys.S:
                    dir -= Vector3.UnitZ;
                    break;
                case Keys.A:
                    dir += Vector3.UnitX;
                    break;
                case Keys.D:
                    dir -= Vector3.UnitX;
                    break;
            }
            Debug.Assert(false, "Not Implemented");
        }

        internal static void HandleMoveButtonDown(InputAction action, GameTime gameTime)
        {
            Vector3 dir = Vector3.Zero;
            switch (action.KeyboardKey)
            {
                case Keys.W:
                    dir -= Vector3.UnitZ;
                    break;
                case Keys.S:
                    dir += Vector3.UnitZ;
                    break;
                case Keys.A:
                    dir -= Vector3.UnitX;
                    break;
                case Keys.D:
                    dir += Vector3.UnitX;
                    break;
            }
            Debug.Assert(false, "Not Implemented");
        }

        internal static void HandleMoveButtonHeld(InputAction action, GameTime gameTime)
        {
            Vector3 dir = Vector3.Zero;
            switch (action.KeyboardKey)
            {
                case Keys.W:
                    dir -= Vector3.UnitZ;
                    break;
                case Keys.S:
                    dir += Vector3.UnitZ;
                    break;
                case Keys.A:
                    dir -= Vector3.UnitX;
                    break;
                case Keys.D:
                    dir += Vector3.UnitX;
                    break;
            }
            //dir *= 1f;
            GameWorld.Player.gameObject.body.isActive = true;
            GameWorld.Player.gameObject.body.applyLinearImpulse(
                GameWorld.Player.gameObject.body.mass * dir);
        }


        internal static void HandleWeaponRotate(InputAction action, GameTime gameTime)
        {
            int torqueDirection = (action.Identifier == "RotateWeaponCCW" ? 1 : 0)
                    + (action.Identifier == "RotateWeaponCW" ? -1 : 0);

            // Update the player's rotation whether they asked for it or
            // not. This is to negate the effects of any rotation caused by
            // collisions.
            GameWorld.Player.weapon.rotation += (float)(torqueDirection * 5 * gameTime.ElapsedGameTime.TotalSeconds);
            GameWorld.Player.rotation = GameWorld.Player.weapon.rotation;
        }

        internal static void HandleAim(InputAction action, Vector2 value, GameTime gameTime)
        {
            // Create a line extending from the camera's position to the cursor's position in 3D.
            Viewport Viewport = GameWorld.Map.Camera.GraphicsDevice.Viewport;
            Matrix Projection = GameWorld.Map.CameraProjection;
            Matrix View = GameWorld.Map.Camera.CameraView;
            Matrix World = Matrix.Identity;

            Vector3 point1 = Viewport.Unproject(new Vector3(value, 0), Projection, View, World);
            Vector3 point2 = Viewport.Unproject(new Vector3(value, 1), Projection, View, World);

            Ray Ray = new Ray(point1, point2 - point1);

            float? hitTime = Ray.Intersects(new Plane(Vector3.UnitY, 0));

            if (hitTime != null)
            {
                Vector3 intersection = Ray.Position + Ray.Direction * (float)hitTime;
                Vector3 aim = GameWorld.Player.gameObject.body.centerOfMass - intersection;

                GameWorld.Player.Cursor.teleportTo(intersection);
                GameWorld.Player.weapon.rotation = (float)(Math.Atan2(aim.Z, -aim.X)); 
                GameWorld.Player.gameObject.body.orientationMatrix = Matrix.CreateRotationY(GameWorld.Player.weapon.rotation);
            }            
        }

        internal static void HandleCenterWeapon(InputAction action, GameTime gameTime)
        {
            GameWorld.Player.weapon.rotation = GameWorld.Player.rotation;
        }

        internal static void MoveZombies(InputAction action, GameTime gameTime)
        {
            GameWorld.MoveZombies = !GameWorld.MoveZombies;
        }
    }
}
